/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 用常量定义三种状态，便于编译器自动提示，也避免写错
const PENDING = 'pending'      // 等待
const RESOLVE = 'resolve'      // 成功
const REJECT = 'reject'        // 失败

class MyPromise {
	constructor(executor) {
		try {
			executor(this.resolve, this.reject)
		} catch (e) {
			this.reject(e)
		}
	}

	status = PENDING		// 初始状态设为“等待” 
	value = undefined		// 成功之后的值，默认undefined
	reason = undefined		// 失败后的原因，默认undefined
	successCallback = []	// 成功后的回调函数
	failCallback = []		// 失败后的回调函数

	resolve = value => {
		// 状态为“等待”，程序才执行，达到状态一经更改，就不可再次更改的目的
		if (this.status === PENDING) {
			this.status = RESOLVE	// 将状态更改为成功
			this.value = value		// 保存成功之后的值
			while (this.successCallback.length) this.successCallback.shift()()	// 判断成功回调是否存在 如果存在 调用
		}
	}
	reject = reason => {
		if (this.status === PENDING) {
			this.status = REJECT	// 将状态更改为失败
			this.reason = reason	// 保存失败后的原因
			while (this.failCallback.length) this.failCallback.shift()()	// 判断失败回调是否存在 如果存在 调用
		}
	}
	then(successCallback, failCallback) {

		// 参数可选
		successCallback = successCallback ? successCallback : value => value
		failCallback = failCallback ? failCallback : reason => { throw reason }
		let promsie2 = new MyPromise((resolve, reject) => {
			// 判断状态
			if (this.status === RESOLVE) {
				setTimeout(() => {
					try {
						let x = successCallback(this.value)
						// 判断 x => 普通值？调用resolve；promise对象？查看promsie对象返回的结果
						resolvePromise(promsie2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			} else if (this.status === REJECT) {
				setTimeout(() => {
					try {
						let x = failCallback(this.reason)
						// 判断 x => 普通值？调用resolve；promise对象？查看promsie对象返回的结果
						resolvePromise(promsie2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			} else {
				// 等待
				// 将成功回调和失败回调存储
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							let x = successCallback(this.value)
							// 判断 x => 普通值？调用resolve；promise对象？查看promsie对象返回的结果
							resolvePromise(promsie2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							let x = failCallback(this.reason)
							// 判断 x => 普通值？调用resolve；promise对象？查看promsie对象返回的结果
							resolvePromise(promsie2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promsie2
	}
	finally(callback) {
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => { throw reason })
		})
	}
	catch(failCallback) {
		return this.then(undefined, failCallback)
	}
	static all(array) {
		let result = []
		let index = 0
		return new MyPromise((resolve, reject) => {
			function addData(key, value) {
				result[key] = value
				index++
				if (index === array.length) {
					resolve(result)
				}
			}
			for (let i = 0; i < array.length; i++) {
				let current = array[i]
				if (current instanceof MyPromise) {
					// promise 对象
					current.then(value => addData(i, value), reason => reject(reason))
				} else {
					// 普通值
					addData(i, array[i])
				}
			}
		})
	}
	static resolve(value) {
		if (value instanceof MyPromise) return value
		return new MyPromise(resolve => resolve(value))
	}
}

function resolvePromise(promsie2, x, resolve, reject) {
	if (promsie2 === x) {
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	}
	if (x instanceof MyPromise) {
		// promise对象
		x.then(resolve, reject)
	} else {
		// 普通值
		resolve(x)
	}
}

// new MyPromise((resolve, reject) => {
// 	resolve(1)
// }).then(res => {
// 	console.log(res)
// })

module.exports = MyPromise